package service

import (
	"fmt"
	"strings"
	"time"

	"gitee.com/openeuler/PilotGo/sdk/logger"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/internal/global"
	"openeuler.org/PilotGo/PilotGo-plugin-automation/pkg/utils"
)

type SubJobStatusCache struct {
	JobId          string    `json:"job_id"`
	IP             string    `json:"ip"`
	JobStatus      string    `json:"job_status"`
	LastUpdateStep int       `json:"last_update_step"`
	TotalStep      int       `json:"total_step"`
	UpdateTime     time.Time `json:"update_time"`
}

func generateSubJobCacheKey(jobId, ip string) string {
	return fmt.Sprintf("job_id:%s:%s", jobId, ip)
}

func lockSubJobKey(jobId string) string {
	return fmt.Sprintf("lock:job:%s", jobId)
}

func SetSubJobCache(jobId string, ips []string, jobStatus string, lastUpdateStep, totalStep int) error {
	lockKey := lockSubJobKey(jobId)
	lockValue := utils.GenerateUUID()
	err := global.App.Redis.AcquireLockWithRetry(lockKey, lockValue, 30*time.Second, 100*time.Millisecond, 5*time.Second)
	if err != nil {
		return fmt.Errorf("获取子作业锁失败: %s", err.Error())
	}
	defer func() {
		err = global.App.Redis.ReleaseLock(lockKey, lockValue)
		if err != nil {
			logger.Error("释放子作业锁失败: %v", err)
		}
	}()
	for _, ip := range ips {
		cache := &SubJobStatusCache{
			JobId:          jobId,
			IP:             ip,
			JobStatus:      jobStatus,
			LastUpdateStep: lastUpdateStep,
			TotalStep:      totalStep,
			UpdateTime:     time.Now(),
		}

		err := global.App.Redis.SetNX(generateSubJobCacheKey(jobId, ip), cache, -1)
		if err != nil {
			return fmt.Errorf("设置子作业缓存失败: %w", err)
		}
	}
	return global.App.Redis.Publish(pubSubChannel, eventSet)
}

func GetSubJobCache(jobId, ip string) (*SubJobStatusCache, error) {
	var cache SubJobStatusCache
	err := global.App.Redis.Get(generateSubJobCacheKey(jobId, ip), &cache)
	if err != nil {
		return nil, err
	}
	return &cache, nil
}

func DeleteSubJobCache(jobId, ip string) error {
	lockKey := lockSubJobKey(jobId)
	lockValue := utils.GenerateUUID()
	err := global.App.Redis.AcquireLockWithRetry(lockKey, lockValue, 30*time.Second, 100*time.Millisecond, 5*time.Second)
	if err != nil {
		return fmt.Errorf("获取子作业锁失败: %s", err.Error())
	}

	err = global.App.Redis.Delete(generateSubJobCacheKey(jobId, ip))
	if err != nil {
		return err
	}

	if err = global.App.Redis.ReleaseLock(lockKey, lockValue); err != nil {
		logger.Warn("释放子作业锁失败: %v", err)
	}

	// 检查是否还有该jobId的其他缓存存在
	pattern := fmt.Sprintf("job_id:%s:*", jobId)
	keys, err := global.App.Redis.Keys(pattern)
	if err != nil {
		logger.Error("检查子作业缓存是否为最后一个时出错: jobId=%s, err=%v", jobId, err)
		return nil
	}

	if len(keys) == 0 {
		logger.Debug("删除的是jobId的最后一个缓存, 发送父作业更新信号: jobId=%s", jobId)

		if err := DeleteJobCache(jobId); err != nil {
			logger.Error("删除作业缓存失败: jobId=%s, err=%v", jobId, err)
		}

		select {
		case jobResultUpdateChan <- jobId:
			logger.Debug("成功发送父作业更新信号: jobId=%s", jobId)
			return global.App.Redis.Publish(pubSubChannel, eventDelete)
		default:
			logger.Warn("作业状态更新通道已满，无法发送最后一个缓存的更新信号: jobId=%s", jobId)
		}
	}
	return nil
}

func UpdateSubJobCache(jobId string, ip string, jobStatus string, lastUpdateStep, totalStep int) error {
	lockKey := lockSubJobKey(jobId)
	lockValue := utils.GenerateUUID()
	err := global.App.Redis.AcquireLockWithRetry(lockKey, lockValue, 30*time.Second, 100*time.Millisecond, 5*time.Second)
	if err != nil {
		return fmt.Errorf("获取作业锁失败: %s", err.Error())
	}
	defer func() {
		err = global.App.Redis.ReleaseLock(lockKey, lockValue)
		if err != nil {
			logger.Warn("释放作业锁失败: %v", err)
		}
	}()

	cache := &SubJobStatusCache{
		JobId:          jobId,
		IP:             ip,
		JobStatus:      jobStatus,
		LastUpdateStep: lastUpdateStep,
		TotalStep:      totalStep,
		UpdateTime:     time.Now(),
	}

	if err := global.App.Redis.Set(generateSubJobCacheKey(jobId, ip), cache, -1); err != nil {
		return err
	}

	return nil
}

func getAllRunningJobsFromRedis() (map[string]string, error) {
	runningJobs := make(map[string]string)

	keys, err := global.App.Redis.Keys("job_id:*")
	if err != nil {
		return nil, err
	}

	for _, key := range keys {
		parts := strings.Split(key, ":")
		if len(parts) >= 3 {
			jobId := parts[1]
			ip := parts[2]
			runningJobs[jobId] = ip
		}
	}
	return runningJobs, nil
}
